/*!
    \copyright  Copyright (c) 2019 - 2025 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file       
    \ingroup    pairing
    \brief      Pairing plugin interface
*/
#include "pairing.h"
#include "pairing_plugin.h"
#include "pairing_plugin_default.h"
#include "pairing_plugin_sm.h"
#include "pairing_private.h"
#include <panic.h>
#include <stdlib.h>
#include <logging.h>
#include <bdaddr.h>

pairing_plugin_t pairing_plugin;

static cl_sm_auth_requirements pairing_ConvertToClAuthReq(uint8 authentication_requirements, bool is_security_manager)
{
    if(is_security_manager)
    {
        /* If using SM for security e.g. BLE or CTKD, then just
         * copy the DM_SM_SECURITY_* bitfield */
        return (cl_sm_auth_requirements)authentication_requirements;
    }
    
    /* Otherwise, convert to the BR/EDR CL type. */
    switch (authentication_requirements)
    {
        case HCI_MITM_NOT_REQUIRED_NO_BONDING:
            return cl_sm_no_bonding_no_mitm;

        case HCI_MITM_REQUIRED_NO_BONDING:
            return cl_sm_no_bonding_mitm;

        case HCI_MITM_NOT_REQUIRED_DEDICATED_BONDING:
            return cl_sm_dedicated_bonding_no_mitm;

        case HCI_MITM_REQUIRED_DEDICATED_BONDING:
            return cl_sm_dedicated_bonding_mitm;

        case HCI_MITM_NOT_REQUIRED_GENERAL_BONDING:
            return cl_sm_general_bonding_no_mitm;

        case HCI_MITM_REQUIRED_GENERAL_BONDING:
            return cl_sm_general_bonding_mitm;

        default:
            return cl_sm_authentication_requirements_unknown;
    }
}

static cl_sm_io_capability pairing_ConvertToClIoCap(uint8 io_capability)
{
    switch (io_capability)
    {
        case HCI_IO_CAP_DISPLAY_ONLY:
            return cl_sm_io_cap_display_only;

        case HCI_IO_CAP_DISPLAY_YES_NO:
            return cl_sm_io_cap_display_yes_no;

        case HCI_IO_CAP_KEYBOARD_ONLY:
            return cl_sm_io_cap_keyboard_only;

        case HCI_IO_CAP_NO_INPUT_NO_OUTPUT:
            return cl_sm_io_cap_no_input_no_output;

        case HCI_IO_CAP_KEYBOARD_DISPLAY:
            return cl_sm_io_cap_keyboard_display;

        default:
            return cl_sm_io_cap_no_input_no_output;
    }
}

void Pairing_PluginInit(void)
{
    pairingTaskData *thePairing = PairingGetTaskData();

    memset(&pairing_plugin, 0, sizeof(pairing_plugin));

    if(thePairing != NULL)
    {
        memset(&thePairing->pairing_clients[0].params.client_plugin, 0, sizeof(thePairing->pairing_clients[0].params.client_plugin));
    }
    
    Pairing_PluginSmContextInit();
}

bool Pairing_PluginIsRegistered(void)
{
    return pairing_plugin.handle_remote_io_capability != NULL;
}

void Pairing_PluginRegister(pairing_plugin_t plugin)
{
    PanicNull((void*)plugin.handle_remote_io_capability);
    PanicNull((void*)plugin.handle_io_capability_req);
    PanicNull((void*)plugin.handle_user_confirmation_req);
    PanicNull((void*)plugin.handle_complete_indication);
    PanicNull((void*)plugin.handle_ssp_complete_indication);

    PanicNotNull((void*)pairing_plugin.handle_remote_io_capability);
    PanicNotNull((void*)pairing_plugin.handle_io_capability_req);
    PanicNotNull((void*)pairing_plugin.handle_user_confirmation_req);
    PanicNotNull((void*)pairing_plugin.handle_complete_indication);
    PanicNotNull((void*)pairing_plugin.handle_ssp_complete_indication);

    pairing_plugin = plugin;
}

bool Pairing_PluginHandleRemoteIoCapability(pairing_plugin_t* pairing_plugin, const CsrBtCmSmIoCapabilityResponseInd *ind)
{
    if(pairing_plugin->handle_remote_io_capability)
    {
        CL_SM_REMOTE_IO_CAPABILITY_IND_T cl_ind = { 0 };
        bool is_security_manager = (ind->flags & DM_SM_FLAGS_SECURITY_MANAGER) != 0;

        BdaddrConvertTpBluestackToVm(&cl_ind.tpaddr, &ind->tp_addrt);
        cl_ind.authentication_requirements =  pairing_ConvertToClAuthReq(ind->authentication_requirements, is_security_manager);
        cl_ind.io_capability = pairing_ConvertToClIoCap(ind->io_capability);
        cl_ind.oob_data_present = ind->oob_data_present;

        pairing_plugin->handle_remote_io_capability(&cl_ind);
        return TRUE;
    }
    return FALSE;
}

bool Pairing_PluginHandleIoCapabilityRequest(pairing_plugin_t* pairing_plugin,const CsrBtCmSmIoCapabilityRequestInd *ind, pairing_plugin_rsp_t* response, pairing_io_capability_rsp_t* io_capability_rsp)
{
    if(pairing_plugin->handle_io_capability_req)
    {
        CL_SM_IO_CAPABILITY_REQ_IND_T cl_ind = { 0 };

        BdaddrConvertTpBluestackToVm(&cl_ind.tpaddr, &ind->tp_addrt);
        cl_ind.sm_over_bredr = ind->flags & DM_SM_FLAGS_SMP_OVER_BREDR;
        cl_ind.link_key_exists = !!(ind->flags & DM_SM_FLAGS_OVERWRITE_BREDR_LINK_KEY);
        *response = pairing_plugin->handle_io_capability_req(&cl_ind, io_capability_rsp);

        return TRUE;
    }
    return FALSE;
}

bool Pairing_PluginHandleUserConfirmationRequest(pairing_plugin_t* pairing_plugin, const CsrBtCmSmUserConfirmationRequestInd *ind, pairing_plugin_rsp_t* response, pairing_user_confirmation_rsp_t* user_confirmation_rsp)
{
    CL_SM_USER_CONFIRMATION_REQ_IND_T cl_ind = { 0 };
    
    if(pairing_plugin->handle_user_confirmation_req)
    {
        BdaddrConvertTpBluestackToVm(&cl_ind.tpaddr, &ind->tp_addrt);
        cl_ind.numeric_value = ind->numeric_value;

        *response = pairing_plugin->handle_user_confirmation_req(&cl_ind, user_confirmation_rsp);
        
        return TRUE;
    }
    
    return FALSE;
}

bool Pairing_PluginHandleCrossTransKeyInd(pairing_plugin_t* pairing_plugin, const CmSmGenerateCrossTransKeyRequestInd *ind, pairing_plugin_rsp_t *plugin_response, pairing_ctkd_rsp_t* ctkd_response)
{
    if(pairing_plugin->handle_ctkd != NULL)
    {
        pairing_ctkd_ind_t pairing_ctkd_ind = { 0 };
        BdaddrConvertTpBluestackToVm(&pairing_ctkd_ind.tpaddr, &ind->tp_addrt);
        *plugin_response = pairing_plugin->handle_ctkd(&pairing_ctkd_ind, ctkd_response);
        return TRUE;
    }
    return FALSE;
}

bool Pairing_PluginHandleSppCompleteIndication(pairing_plugin_t* pairing_plugin, const CsrBtCmSmSimplePairingCompleteInd* ind, pairing_plugin_rsp_t* response, pairing_ssp_complete_rsp_t* bredr_bond_rsp)
{
    if(pairing_plugin->handle_ssp_complete_indication)
    {
        pairing_ssp_complete_ind_t spp_complete_ind;
        memset(&spp_complete_ind, 0, sizeof(spp_complete_ind));
        BdaddrConvertTpBluestackToVm(&spp_complete_ind.tpaddr, &ind->tp_addrt);
        spp_complete_ind.status = ind->status;
        
        *response = pairing_plugin->handle_ssp_complete_indication(&spp_complete_ind, bredr_bond_rsp);
        return TRUE;
    }
    return FALSE;
}

void Pairing_PluginPairingComplete(pairing_plugin_t* pairing_plugin)
{
    if(pairing_plugin && pairing_plugin->handle_complete_indication)
    {
        pairing_plugin->handle_complete_indication();
    }
}

void Pairing_PluginUnregister(pairing_plugin_t plugin)
{
    PanicFalse(pairing_plugin.handle_remote_io_capability == plugin.handle_remote_io_capability);
    PanicFalse(pairing_plugin.handle_io_capability_req == plugin.handle_io_capability_req);
    PanicFalse(pairing_plugin.handle_user_confirmation_req == plugin.handle_user_confirmation_req);
    PanicFalse(pairing_plugin.handle_complete_indication == plugin.handle_complete_indication);
    PanicFalse(pairing_plugin.handle_ssp_complete_indication == plugin.handle_ssp_complete_indication);
    memset(&pairing_plugin, 0, sizeof(pairing_plugin));
    
    /* Kick retry in case we were waiting on a response from the plugin that unregistered */
    (void)Pairing_PluginRetry();
}
